<?php

declare(strict_types=1);

/* (c) Anton Medvedev <anton@medv.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Deployer\Documentation;

class ApiGen
{
    /**
     * @var array
     */
    private $fns = [];

    public function parse(string $source): void
    {
        $comment = '';
        $params = '';
        $signature = '';

        $source = str_replace("\r\n", "\n", $source);

        $state = 'root';
        foreach (explode("\n", $source) as $lineNumber => $line) {
            switch ($state) {
                case 'root':
                    if (str_starts_with($line, '/**')) {
                        $state = 'comment';
                        break;
                    }
                    if (str_starts_with($line, 'function')) {
                        $signature = preg_replace('/^function\s+/', '', $line);
                        $funcName = preg_replace('/\(.*$/', '', $signature);
                        $this->fns[] = [
                            'comment' => $comment,
                            'params' => $params,
                            'funcName' => $funcName,
                            'signature' => $signature,
                        ];
                        $comment = '';
                        $params = '';

                        if (str_ends_with($signature, '(')) {
                            $state = 'params';
                        } else {
                            $signature = '';
                        }
                    }
                    break;

                case 'comment':
                    if (str_ends_with($line, '*/')) {
                        $state = 'root';
                        break;
                    }
                    if (preg_match('/^\s\*\s@param\s(?<type>.+?)\$(?<name>.+?)\s(?<comment>.+)$/', $line, $matches)) {
                        if (empty($params)) {
                            $params = "| Argument | Type | Comment |\n|---|---|---|\n";
                        }
                        $type = implode(' or ', array_map(function ($t) {
                            $t = trim($t, ' ');
                            return "`$t`";
                        }, explode('|', $matches['type'])));
                        $params .= "| `\${$matches['name']}` | $type | {$matches['comment']} |\n";
                        break;
                    }
                    if (str_starts_with($line, ' * @')) {
                        break;
                    }
                    $comment .= preg_replace('/^\s\*\s?/', '', $line) . "\n";
                    break;

                case 'params':
                    if (preg_match('/^\).+\{$/', $line, $matches)) {
                        $signature .= "\n" . preg_replace('/\{$/', '', $line);
                        $this->fns[count($this->fns) - 1]['signature'] = $signature;
                        $state = 'root';
                    } else {
                        $signature .= "\n" . $line;
                    }
                    break;
            }
        }
    }

    public function markdown(): string
    {
        $output = <<<MD
            <!-- DO NOT EDIT THIS FILE! -->
            <!-- Instead edit src/functions.php -->
            <!-- Then run bin/docgen -->

            # API Reference


            MD;

        foreach ($this->fns as $fn) {
            [
                'comment' => $comment,
                'params' => $params,
                'funcName' => $funcName,
                'signature' => $signature,
            ] = $fn;

            if (!empty($params)) {
                $params = "\n$params";
            }

            $output .= <<<MD
                ## $funcName()

                ```php
                $signature
                ```

                $comment
                $params

                MD;
        }
        return $output;
    }
}
